Skip to content

Add Export as a Dual of Import #6169

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Apr 4, 2019
Merged

Conversation

odersky
Copy link
Contributor

@odersky odersky commented Mar 26, 2019

This is a pre-SIP proposal and its implementation to introduce export clauses to the language.

Motivation and details are in the export.md file of this PR.

I co-developed proposal and implementation because the type checking details are a bit tricky, so it paid to have a concrete algorithm to work on that can be tested.

This pre-SIP addresses what must be the longest standing proposal to improve the Scala language. The suggestion to have something like this was first brought up by Jamie Webb in 2005.

Cf also the pre-SIP by David Barry: https://github.com/japgolly/SIP/blob/master/SIP-xx-export/SIP.md

@julienrf
Copy link
Contributor

julienrf commented Mar 26, 2019

This is interesting. Can we use it to define aliases within the same class?

trait IterableOps[+A, +CC[_], +C] {

  def concat[B >: A](other: IterableOnce[B]): CC[B]

  export this.{concat => ++}

}

Note that a difference with the examples you mentioned is that class members can be refined, in which case the alias should be refined accordingly:

trait X[+A] extends IterableOps[A, Seq, List[A]] {

  def concat[B >: A](other: IterableOnce[B]): List[B] = ... // note that return type is `List[B]` instead of the inherited `Seq[B]`

}

@odersky
Copy link
Contributor Author

odersky commented Mar 26, 2019

Note that a difference with the examples you mentioned is that class members can be refined, in which case the alias should be refined accordingly

No, export is not made for that. I think what you are looking for in this use case is @alpha (#5975).

@sjrd
Copy link
Member

sjrd commented Mar 26, 2019

Should we have a Contributors thread to discuss the proposal, independently of its implementation?

In the meantime:

Type members are aliased by type definitions, and term members are aliased by method definitions. Export aliases copy the type and value parameters of the members they refer to.

So, exports basically copy members. IIUC that means we cannot use export to re-export towers of implicits with decreasing priorities in a hierarchy of classes (the XYZLowPriorityImplicits pattern), as the exports will all end up in the same level of priority, even when the imported members had different levels of priority. This means that it is impossible to use export to replace the use of package object with extends that use the LowPriorityImplicits pattern. Now, that pattern is the only strong reason that we could not already replace all package objects by top-level definitions (albeit with more boilerplate than with export). So IIUC export does not actually solve the loss of API design expressivity incurred by dropping package object ... extends.

@odersky
Copy link
Contributor Author

odersky commented Mar 26, 2019

@srjd Yes, I was going to open a thread on contributors.

Regarding implicits, we fortunately will have better ways now to prioritze than by location of original definition.: #6071 (comment).

@odersky odersky force-pushed the add-export-2 branch 2 times, most recently from e404bd9 to f36c5d9 Compare March 28, 2019 15:00
It is a compile-time error if a simple or renaming selector does not identify any eligible
members.

Type members are aliased by type definitions, and term members are aliased by method definitions. Export aliases copy the type and value parameters of the members they refer to.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about annotations, access modifiers, and the inline modifier?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None of them are copied (I believe that's implied by the text).

1. Elaborate any annotations of the class.
2. Elaborate the parameters of the class.
3. Elaborate the self type of the class, if one is given.
4. Enter all definitions of the class as class members, with types to be completed
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From this it's not very clear if definitions in classes can use exported aliases...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They can, but paths to exported aliases cannot refer to other exported aliases.

val maybeStable = if (mbr.symbol.isStableMember) StableRealizable else EmptyFlags
ctx.newSymbol(
cls, alias,
Method | Final | maybeStable | mbr.symbol.flags & ImplicitOrImplied,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Synthetic?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I don't think we want to make them Synthetic .

val sym = mbr.symbol
if (sym.is(ImplicitOrImplied) != exp.impliedOnly) s"is ${if (exp.impliedOnly) "not " else ""}implied"
else if (!sym.isAccessibleFrom(path.tpe)) "is not accessible"
else if (sym.isConstructor || sym.is(ModuleClass) || sym.is(Bridge)) "_"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing error message?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's intended to be skipped later. I'll change it to something less obscure.

Copier.cfg
Copier.config
Copier.config2
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to also have an example with inline since it was mentioned in the meeting.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inline is not implemented in this PR. Not clear we want it.

@soronpo
Copy link
Contributor

soronpo commented Apr 2, 2019

Proposal for error test cases:

class Foo {
  lazy foo : Foo = new Foo
  export foo._
}
class Foo {
  lazy bar: Bar = new Bar
  export bar._
}
class Bar {
  lazy foo: Foo = new Foo
  export foo._
}

@odersky odersky merged commit 5b0a7ce into scala:master Apr 4, 2019
@allanrenucci allanrenucci deleted the add-export-2 branch April 4, 2019 17:01
@biboudis biboudis added this to the 0.14 Tech Preview milestone Apr 5, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants